package org.etlt.load;

import org.apache.commons.lang3.StringUtils;
import org.etlt.extract.ExtractorSetting;
import org.etlt.job.JobContext;
import org.etlt.job.config.xml.AbstractXmlResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LoaderSettingXmlResolver extends AbstractXmlResolver<LoaderSetting> {

    @Override
    public List<LoaderSetting> resolveBundle(InputStream stream, JobContext context) {
        try {
            Document document = getDocumentBuilder().parse(stream);
            Element root = document.getDocumentElement();
            if (root.getNodeName().equals("loaders")) {
                List<LoaderSetting> bundleSettings = new ArrayList<>();
                NodeList children = root.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    Node node = children.item(i);
//                    NodeParser sp = getParserFactory().get(getXPathExpression(node));
//                    LoaderSetting setting = (LoaderSetting) sp.parse(null, node, context);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        LoaderSetting setting = parseElementForcedly(null, (Element) node, context);
                        bundleSettings.add(setting);
                        NodeList loaderNode = node.getChildNodes();
                        for (int j = 0; j < loaderNode.getLength(); j++) {
                            Node childOfLoader = loaderNode.item(j);
                            if (childOfLoader.getNodeType() == Node.ELEMENT_NODE) {
                                parseElementFriendly(setting, (Element) loaderNode.item(j), context);
                            }
                        }
                    }
                }
                return bundleSettings;
            } else return super.resolveBundle(stream, context);
        } catch (SAXException | IOException e) {
            throw new IllegalStateException("job.xml parse error.", e);
        }
    }

    @Override
    protected void registerParser() {

        registerParser((setting, node, context) -> {
                    Element element = (Element) node;
                    String type = element.getAttribute("type");
                    String name = element.getAttribute("name");
                    switch (type) {
                        case ExtractorSetting.DATA_BASE_TYPE:
                        case ExtractorSetting.BIG_DATA_BASE_TYPE:
                            DatabaseLoaderSetting setting1 = new DatabaseLoaderSetting();
                            setting1.setName(name);
                            setting1.setDatasource(getFirstChildValueByName(element, "datasource"));
                            setting1.setParallel(Integer.parseInt(getFirstChildValueByName(element, "parallel", "1")));
                            setting1.setAutoResolve(Boolean.parseBoolean(getFirstChildValueByName(element, "autoResolve")));
                            setting1.setBatch(Integer.parseInt(context.getPropertyValue(getFirstChildValueByName(element, "batch"))));
                            List<String> depends = Arrays.asList(StringUtils.split(getAttributeValue(element, "depends"), ","));
                            setting1.setDependedLoader(depends);
                            return setting1;
                        case ExtractorSetting.FILE_TYPE:
                            throw new UnsupportedOperationException("FILE is not supported now.");
                        default:
                            break;
                    }
                    throw new UnsupportedOperationException("unsupported loader type: " + type);
                }, "/databaseLoader",
                "/loaders/databaseLoader");

        registerParser((setting, node, context) -> {
                    Element element = (Element) node;
                    String type = element.getAttribute("type");
                    String name = element.getAttribute("name");
                    switch (type) {
                        case ExtractorSetting.DATA_BASE_TYPE:
                            throw new UnsupportedOperationException("DATABASE is not supported for fileLoader.");
                        case ExtractorSetting.FILE_TYPE:
                            FileLoaderSetting setting1 = new FileLoaderSetting();
                            setting1.setName(name);
                            setting1.setDelim(getFirstChildValueByName(element, "delim"));
                            setting1.setOutputNull(getFirstChildValueByName(element, "outputNull"));
                            setting1.setUsingBanner(Boolean.valueOf(getFirstChildValueByName(element, "usingBanner", "false")));
                            setting1.setDatasource(getFirstChildValueByName(element, "datasource"));
                            setting1.setAutoResolve(Boolean.parseBoolean(getFirstChildValueByName(element, "autoResolve")));
                            List<String> depends = Arrays.asList(StringUtils.split(getAttributeValue(element, "depends"), ","));
                            setting1.setDependedLoader(depends);
                            return setting1;
                        default:
                            break;
                    }
                    throw new UnsupportedOperationException("unsupported loader type: " + type);
                }, "/fileLoader",
                "/loaders/fileLoader");


        registerParser((setting, node, context) -> {
                    String content = node.getTextContent();
                    setting.setExtractor(content);
                    return setting;
                }, "/databaseLoader/extractor", "/fileLoader/extractor",
                "/loaders/databaseLoader/extractor", "/loaders/fileLoader/extractor");

        registerParser((setting, node, context) -> {
                    Element element = (Element) node;
                    NodeList nodeList = element.getElementsByTagName("column");
                    List<ColumnSetting> columnSettings = new ArrayList<>();
                    for (int i = 0; i < nodeList.getLength(); i++) {
                        Element column = (Element) nodeList.item(i);
                        String name = column.getAttribute("name");
                        String expression = column.getTextContent();
                        ColumnSetting columnSetting = new ColumnSetting(name + ColumnSetting.EQUAL + expression);
                        columnSettings.add(columnSetting);
                    }
                    String removes = element.getAttribute("removes");
                    if (!StringUtils.isBlank(removes)) {
                        String toRemoves[] = StringUtils.split(removes, ",");
                        setting.setRemoveColumns(Arrays.asList(toRemoves));
                    }
                    setting.setColumns(columnSettings);
                    return setting;
                }, "/databaseLoader/columns",
                "/loaders/databaseLoader/columns");

        registerParser((setting, node, context) -> {
                    setting.setDatasource(node.getTextContent());
                    return setting;
                }, "/databaseLoader/datasource",
                "/loaders/databaseLoader/datasource");

        registerParser((setting, node, context) -> {
                    Element element = (Element) node;
                    ((DatabaseLoaderSetting) setting).addPredml(element.getTextContent());
                    return setting;
                }, "/databaseLoader/preDml",
                "/loaders/databaseLoader/preDml");

        registerParser((setting, node, context) -> {
                    Element element = (Element) node;
                    DmlSetting dml = new DmlSetting(element.getAttribute("id"));
                    dml.setDml(element.getTextContent());
                    dml.setWhen(element.getAttribute("when"));
                    dml.setTolerate(Boolean.valueOf(element.getAttribute("tolerate")));
                    dml.setUsingExtractorColumns(Boolean.valueOf(element.getAttribute("usingExtractorColumns")));
                    dml.setMultiValue(Boolean.valueOf(element.getAttribute("multiValue")));
                    dml.setRepeat(Integer.parseInt(element.getAttribute("repeat")));
                    List<Element> entityElements = LoaderSettingXmlResolver.this.getElementsByName(element, "entity");
                    entityElements.forEach(entityElement->{
                        String name = entityElement.getAttribute("name");
                        String expression = entityElement.getAttribute("expression");
                        DmlEntitySetting dmlEntitySetting = new DmlEntitySetting(name);
                        dmlEntitySetting.setExpression(expression);
                        dml.addEntitySetting(dmlEntitySetting);
                    });
                    dml.check();
                    ((DatabaseLoaderSetting) setting).addDml(dml);
                    return setting;
                }, "/databaseLoader/dml",
                "/loaders/databaseLoader/dml");

    }
}
